﻿using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Win32.SafeHandles;
using System.IO;
using System.Threading;
using System.Runtime.InteropServices;
using NLog;
using NLog.Win32.Targets;
using NLog.Targets;
using NLog.Targets.Compound;
using System.Reflection;

namespace Donsole
{
    public static class Donsole
    {
        private static Logger logger = LogManager.GetLogger("DonsoleLogger");
        private static bool loggerReady = false;
        private static bool startDonsole = false;

        public static Logger Logger
        {
            get
            {
                if (!loggerReady)
                {
                    ColoredConsoleTarget consoleTarget = new ColoredConsoleTarget();
                    consoleTarget.Layout = "${date:format=HH\\:MM\\:ss} ${message} ${exception:format=tostring}";

                    consoleTarget.RowHighlightingRules.Add(
                            new ConsoleRowHighlightingRule(
                                "level >= LogLevel.Info", // condition
                                ConsoleOutputColor.Gray, // foreground color
                                ConsoleOutputColor.NoChange) // background color
                            );

                    consoleTarget.RowHighlightingRules.Add(
                            new ConsoleRowHighlightingRule(
                                "level >= LogLevel.Error", // condition
                                ConsoleOutputColor.Red, // foreground color
                                ConsoleOutputColor.NoChange) // background color
                            );

                    consoleTarget.RowHighlightingRules.Add(
                            new ConsoleRowHighlightingRule(
                                "level >= LogLevel.Debug", // condition
                                ConsoleOutputColor.Magenta, // foreground color
                                ConsoleOutputColor.NoChange) // background color
                            );

                    FileTarget fileTarget = new FileTarget();
                    fileTarget.Layout = "${date:format=HH\\:MM\\:ss} ${message} ${exception:format=tostring}";
                    fileTarget.FileName = "${basedir}/logs/${date:format=yyyy}/${date:format=MM}/${date:format=yyyyMM_dd}.txt";
                    fileTarget.KeepFileOpen = false;
                    fileTarget.Encoding = "iso-8859-2";
                    fileTarget.EnableFileDelete = false;

                    SplitTarget target = new SplitTarget(fileTarget);
                    if (startDonsole)
                        target.Targets.Add(consoleTarget);

                    NLog.Config.SimpleConfigurator.ConfigureForTargetLogging(target, LogLevel.Trace);
                    //LogManager.Configuration = new NLog.Config.XmlLoggingConfiguration("ColoredConsoleTargetRowHighlighting.nlog");

                    AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);

                    loggerReady = true;
                }
                return logger;
            }
        }

        static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
        {
            Logger.DebugException("Exception:", e.ExceptionObject as Exception);
        }

        private static IntPtr handle = IntPtr.Zero;
        public static void Start(bool alwaysOnTop)
        {
            AllocConsole();
            IntPtr stdHandle = GetStdHandle(STD_OUTPUT_HANDLE);
            SafeFileHandle safeFileHandle = new SafeFileHandle(stdHandle, true);
            FileStream fileStream = new FileStream(safeFileHandle, FileAccess.Write);
            Encoding encoding = System.Text.Encoding.GetEncoding(MY_CODE_PAGE);
            StreamWriter standardOutput = new StreamWriter(fileStream, encoding);
            standardOutput.AutoFlush = true;
            Console.SetOut(standardOutput);

            if (alwaysOnTop)
            {
                string originalTitle = Console.Title;
                string uniqueTitle = Guid.NewGuid().ToString();
                Console.Title = uniqueTitle;
                Thread.Sleep(50);
                handle = FindWindowByCaption(IntPtr.Zero, uniqueTitle);

                if (handle == IntPtr.Zero)
                {
                    Console.WriteLine("Unable to find main window.");
                    return;
                }

                Console.Title = originalTitle;
                SetWindowPos(handle, HWND_TOPMOST, 0, 0, 0, 0, TOPMOST_FLAGS);
            }

            startDonsole = true;
        }

        #region Win32 Calls
        static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
        const UInt32 SWP_NOSIZE = 0x0001;
        const UInt32 SWP_NOMOVE = 0x0002;
        const UInt32 TOPMOST_FLAGS = SWP_NOMOVE | SWP_NOSIZE;

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);

        [DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]
        static extern IntPtr FindWindowByCaption(IntPtr zeroOnly, string lpWindowName);

        [DllImport("kernel32.dll",
            EntryPoint = "GetStdHandle",
            SetLastError = true,
            CharSet = CharSet.Auto,
            CallingConvention = CallingConvention.StdCall)]
        private static extern IntPtr GetStdHandle(int nStdHandle);

        [DllImport("kernel32.dll",
            EntryPoint = "AllocConsole",
            SetLastError = true,
            CharSet = CharSet.Auto,
            CallingConvention = CallingConvention.StdCall)]
        private static extern int AllocConsole();

        private const int STD_OUTPUT_HANDLE = -11;
        private const int MY_CODE_PAGE = 437;
        #endregion
    }
}
